/*
 * Copyright  2005-2006 The Keyintegrity
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.keyintegrity.webbeans;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.tapestry.IAsset;
import org.apache.tapestry.IMarkupWriter;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.components.Block;
import org.apache.tapestry.markup.MarkupWriterImpl;
import org.apache.tapestry.markup.UTFMarkupFilter;

public abstract class XsltBeanEditor extends BeanEditor {
    
    public abstract IAsset getStylesheet();
    public abstract void setStylesheet(IAsset stylesheet);
    
    public abstract boolean isDebugXml();
    
    @Override
    public boolean isRenderXml() {
        return true;
    }
    
    @Override
    public Block getNestedBeanEditor() {
        IRequestCycle cycle = getPage().getRequestCycle();
        
        String nbePageName = getNamespace().getId() + ":XsltBeanEditorPool"; 
         
        BeanEditorPool nbe = (BeanEditorPool) cycle.getPage(nbePageName);
        
        return nbe.acquireBeanEditorBlock(this, getBeanFieldName());
    }
    
    @Override
    public void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {
        if (cycle.isRewinding()) {
            super.renderComponent(writer, cycle);
            return;
        }
        
        ByteArrayOutputStream defaultOutput = new ByteArrayOutputStream();
        
        renderDefault(cycle, defaultOutput);
        
        if (! isDebugXml()
                //  Do not render nested XsltBeanEditors, do it once on the top level instead
                && getParent() == null) {
            ByteArrayInputStream xmlInput = new ByteArrayInputStream(defaultOutput.toByteArray());
            ByteArrayOutputStream xsltOutput = new ByteArrayOutputStream();
            
            try {
                renderLayout(xmlInput, xsltOutput);
            } catch (TransformerFactoryConfigurationError e) {
                throw new ApplicationRuntimeException(e.getMessage(), e);
            } catch (TransformerException e) {
                throw new ApplicationRuntimeException(e.getMessage(), e);
            }
        
            writer.print(xsltOutput.toString(), true);
        } else {
            if (getParent() != null) {
                writer.print(defaultOutput.toString(), true);
            } else {
                writer.print(defaultOutput.toString());
            }
        }
    }

    private void renderLayout(ByteArrayInputStream xmlInput, ByteArrayOutputStream xsltOutput)
        throws TransformerFactoryConfigurationError, TransformerConfigurationException, TransformerException
    {
        Source xmlSource = new StreamSource(xmlInput);
        Source xsltSource = new StreamSource(getStylesheet().getResourceAsStream());
        Result xsltResult = new StreamResult(xsltOutput);
        
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer(xsltSource);
        transformer.transform(xmlSource, xsltResult);
    }

    private void renderDefault(IRequestCycle cycle, ByteArrayOutputStream renderedComponent) {
        IMarkupWriter buffer = new MarkupWriterImpl("text/xml",
                new PrintWriter(renderedComponent), new UTFMarkupFilter());
        
        super.renderComponent(buffer, cycle);
        
        buffer.flush();
    }
}
